package service

import (
	"GopherFocus/app/dao"
	"GopherFocus/app/model"
	"GopherFocus/app/shared"
	"GopherFocus/app/system/index/internal/define"
	"context"
	"github.com/gogf/gf/database/gdb"
	"github.com/gogf/gf/frame/g"
	"github.com/gogf/gf/util/gutil"
)

/*
1.需要查询的返回结果中存在多个字段，A，B，C假设彼此之间是一对一关系
2.首先根据前端输入参数获取A的列表，存入结果结构体列表中
3.从A列表中获取出BId列表，然后查询所有的B
4.B通过id与A的BId进行关联，使得结构结构体列表中的每一项中的A均能匹配到对应的B
*/

var Reply = replyService{}

type replyService struct{}

// 获取回复列表
func (s *replyService) GetList(ctx context.Context, input define.ReplyGetListInput) (output *define.ReplyGetListOutput, err error) {
	// 获取分页码和每页数量
	output = &define.ReplyGetListOutput{
		Page: input.Page,
		Size: input.Size,
	}
	// 传入Fields()中的字段相当于select的字段
	m := dao.Reply.Ctx(ctx).Fields(model.ReplyListItem{})
	if input.TargetType != "" {
		m = m.Where(dao.Reply.C.TargetType, input.TargetType)
	}
	// 目标ID(对应ContentId)
	if input.TargetId > 0 {
		m = m.Where(dao.Reply.C.TargetId, input.TargetId)
	}
	if input.UserId > 0 {
		m = m.Where(dao.Reply.C.UserId, input.UserId)
	}

	// ScanList(&output.List, "Reply")将查询出来的结果绑定到output.List每个元素的
	err = m.Page(input.Page, input.Size).OrderDesc(dao.Content.C.Id).ScanList(&output.List, "Reply")
	if err != nil {
		return nil, err
	}
	if len(output.List) == 0 {
		return nil, nil
	}
	// 为什么上面获取了一次下面还需要获取???
	if err = m.ScanList(&output.List, "Reply"); err != nil {
		return nil, err
	}
	// 根据Reply的UserId关联User的Id
	// 第一步：Reply与User是一对一关系，已经获取的Reply，现在需要根据Reply的UserId与User的Id进行关联，获取User
	// 第二步：先根据gutil.ListItemValuesUnique(output.List, "Reply", "UserId")从reply中获取所有的UserId
	// 第三步：根据UserId列表获取User
	// 第四步：根据User的ID与Reply的UserId进行关联，使结果结构体获取到Reply所对应的User
	// 关联时Fields结构体命名: ReplyListUserItem{}  用Reply的UserId关联User的Id
	err = dao.User.
		Fields(model.ReplyListUserItem{}).
		Where(dao.User.C.Id, gutil.ListItemValuesUnique(output.List, "Reply", "UserId")).
		ScanList(&output.List, "User", "Reply", "id:UserId")
	if err != nil {
		return nil, err
	}

	// 根据Reply的TargetId关联Content的Id
	err = dao.Content.Fields(dao.Content.C.Id, dao.Content.C.Title, dao.Content.C.CategoryId).
		Where(dao.Content.C.Id, gutil.ListItemValuesUnique(output.List, "Reply", "TargetId")).
		ScanList(&output.List, "Content", "Reply", "id:TargetId")
	if err != nil {
		return nil, err
	}

	// 根据Content的CategoryId关联Category的Id
	err = dao.Category.Fields(model.ContentListCategoryItem{}).
		Where(dao.Category.C.Id, gutil.ListItemValuesUnique(output.List, "Content", "CategoryId")).
		ScanList(&output.List, "Category", "Content", "id:CategoryId")
	if err != nil {
		return nil, err
	}

	return output, nil

}

// 创建回复
func (s *replyService) Create(ctx context.Context, input define.ReplyDoCreateInput) error {
	return dao.Reply.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 覆盖用户ID???
		input.UserId = shared.Context.Get(ctx).User.Id
		// 插入数据
		_, err := dao.Reply.Ctx(ctx).Data(input).Insert()
		if err == nil {
			err = Content.AddReplyCount(ctx, input.TargetId, 1)
		}
		return err
	})
}

// 删除回复(硬删除)
func (s *replyService) DoDelete(ctx context.Context, id uint) error {
	return dao.Reply.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		var reply *model.Reply
		err := dao.Reply.Ctx(ctx).WherePri(id).Scan(&reply)
		if err != nil {
			return err
		}
		// 删除回复记录
		_, err = dao.Reply.Ctx(ctx).Where(g.Map{
			dao.Reply.C.Id:     id,
			dao.Reply.C.UserId: shared.Context.Get(ctx).User.Id,
		}).Delete()
		if err == nil {
			// 回复统计-1
			err = Content.AddReplyCount(ctx, reply.TargetId, -1)
			if err != nil {
				return err
			}
			// 判断回复是否采纳
			var content *model.Content
			err = dao.Content.Ctx(ctx).WherePri(reply.TargetId).Scan(&content)
			if err == nil && content != nil && content.AdoptedReplyId == id {
				err = Content.UnacceptedReply(ctx, reply.TargetId)
			}
		}
		return err
	})
}

// 删除回复（硬删除)-通过UserId和ContentId删除
func (s *replyService) DeleteByUserContentId(ctx context.Context, userId, contentId uint) error {
	return dao.Reply.Transaction(ctx, func(ctx context.Context, tx *gdb.TX) error {
		// 删除内容对应的回复
		_, err := dao.Reply.Ctx(ctx).Where(g.Map{
			dao.Reply.C.TargetId: contentId,
			dao.Reply.C.UserId:   userId,
		}).Delete()
		return err
	})
}
